﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using SyncSocketModuleCore.SyncSocketModule.Event;
using System.Net.Http;

namespace SyncSocketModuleCore.SyncSocketModule
{
    public delegate void OnSocketReceiveHandle(string receiveData);
    public delegate void OnClientConnectHandle(bool Connected);
    public class SocketClient
    {
        protected Socket m_oSocket;

       

        private string IP = "";

        private int PORT;

        private byte[] receiveBuff = new byte[1024];

        private byte[] protocolReceiveBuff = new byte[1024];

        public IAsyncResult Asr { get; set; }

    
        public event OnSocketReceiveHandle TcpSyncSocketReceive;

        public event OnClientConnectHandle TcpSyncSocketConnected;

        public event EventHandler<TcpSyncSocketEventArgs> OnWriteLogInfo;

        public event EventHandler<TcpSyncSocketEventArgs> OnWriteLogException;

        public event EventHandler<TcpSyncSocketEventArgs> OnConnectHandler;

        public event EventHandler<TcpSyncSocketEventArgs> OnCloseHandler;

        public SocketClient()
        {
        }

        public SocketClient(string _IP, int _PORT)
        {
            IP = _IP;
            PORT = _PORT;
        }

        public void SocketConfigUpdate(string _IP, int _PORT)
        {
            IP = _IP;
            PORT = _PORT;
        }

        public void WriteLogException(string _strLog, string _ErrorMsg)
        {
            TcpSyncSocketEventArgs Contents = new TcpSyncSocketEventArgs();
            Contents.Message = _strLog;
            Contents.ErrorMsg = _ErrorMsg;
            if (this.OnWriteLogException != null)
            {
                this.OnWriteLogException(this, Contents);
            }
        }

        public void WriteLogInfo(string _strLog)
        {
            TcpSyncSocketEventArgs Contents = new TcpSyncSocketEventArgs();
            Contents.Message = _strLog;
            Contents.ErrorMsg = "";
            if (this.OnWriteLogInfo != null)
            {
                this.OnWriteLogInfo(this, Contents);
            }
        }

        public void OnConnect(Exception e)
        {
            TcpSyncSocketEventArgs Contents = new TcpSyncSocketEventArgs();
            if (e == null)
            {
                Contents.Message = "OnConnect()";
                Contents.ErrorMsg = "";
            }
            else
            {
                Contents.Message = "OnConnect() Exception";
                Contents.ErrorMsg = e.StackTrace;
            }
            if (this.OnConnectHandler != null)
            {
                this.OnConnectHandler(this, Contents);
            }
        }

        public void OnClose(Exception e)
        {
            TcpSyncSocketEventArgs Contents = new TcpSyncSocketEventArgs();
            if (e == null)
            {
                Contents.Message = "OnClose()";
                Contents.ErrorMsg = "";
            }
            else
            {
                Contents.Message = "OnClose Exception";
                Contents.ErrorMsg = e.StackTrace;
            }
            if (this.OnCloseHandler != null)
            {
                this.OnCloseHandler(this, Contents);
            }
        }

      

       

        public bool IsConnected()
        {
            try
            {
                if (m_oSocket == null) return false;
                if (m_oSocket.Connected == false ||
                          m_oSocket.Poll(1, SelectMode.SelectRead) == true && m_oSocket.Available == 0)
                {
                    return false;
                }
                return true;
            }
            catch(Exception ex)
            {
                return false;
            }
        }

        public bool Connect(string strIP, int nPort)
        {
            if (IsConnected())
            {
                if (m_oSocket.RemoteEndPoint != null)
                {
                    WriteLogInfo($"{m_oSocket.RemoteEndPoint.ToString()}  Is Already Conneted Socket!");
                }
                return true;
            }
            Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                IPEndPoint ep = new IPEndPoint(IPAddress.Parse(strIP), nPort);
                if (Asr != null && !Asr.IsCompleted)
                {
                    return false;
                }
                sock.ReceiveTimeout = 3000;
                sock.SendTimeout = 3000;
                Asr = sock.BeginConnect(ep, AsyncOnConnected, sock);
                Asr.AsyncWaitHandle.WaitOne(5000, exitContext: true);
            }
            catch (Exception e)
            {
                OnConnect(e);
            }
            return sock.Connected;
        }

        public bool Connect()
        {
            if (IsConnected())
            {
                if (m_oSocket.RemoteEndPoint != null)
                {
                    WriteLogInfo($"{m_oSocket.RemoteEndPoint.ToString()}  Is Already Conneted Socket!");
                }
                return true;
            }
            Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                IPEndPoint ep = new IPEndPoint(IPAddress.Parse(IP), PORT);
                if (Asr != null && !Asr.IsCompleted)
                {
                    return false;
                }
                sock.ReceiveTimeout = 5000;
                sock.SendTimeout = 5000;
                Asr = sock.BeginConnect(ep, AsyncOnConnected, sock);
                Asr.AsyncWaitHandle.WaitOne(5000, exitContext: true);
            }
            catch (Exception e)
            {
                OnConnect(e);
            }
            return sock.Connected;
        }

        private void AsyncOnConnected(IAsyncResult _ar)
        {
            Socket sock = (Socket)_ar.AsyncState;
            try
            {
                m_oSocket = sock;
                if (m_oSocket.Connected)
                {
                    sock.EndConnect(_ar);
                }
                OnConnect(null);
                if (m_oSocket.Connected)
                {
                    Receive();
                }
            }
            catch (Exception ex)
            {
                OnConnect(ex);
            }
            if (this.TcpSyncSocketConnected != null)
            {
                this.TcpSyncSocketConnected(m_oSocket.Connected);
            }
        }

        public void Close()
        {
            if (m_oSocket == null)
            {
                return;
            }
            try
            {
                if (this.TcpSyncSocketConnected != null)
                {
                    this.TcpSyncSocketConnected(Connected: false);
                }
                if (m_oSocket.Connected)
                {
                    m_oSocket.Shutdown(SocketShutdown.Both);
                    m_oSocket.Close(1000);
                }
            }
            catch (Exception e)
            {
                OnClose(e);
                return;
            }
            OnClose(null);
        }

        public void Clear()
        {
            if (m_oSocket != null && m_oSocket.Available > 0)
            {
                byte[] buffer = new byte[m_oSocket.Available];
                m_oSocket.Receive(buffer);
            }
        }

        private void ClearReadBuffer()
        {
            Array.Clear(receiveBuff, 0, 1024);
        }

        private void PropertocolClearReadBuffer()
        {
            for (int i = 0; i < protocolReceiveBuff.Length; i++)
            {
                protocolReceiveBuff[i] = 0;
            }
        }

        public void Send(string Packet)
        {
            byte[] SendBuffer = Encoding.ASCII.GetBytes(Packet);
            Send(SendBuffer, 0, SendBuffer.Length, SocketFlags.None);
        }

        public void Send(byte[] buffer, int offset, int size, SocketFlags flag)
        {
            if (m_oSocket == null || !m_oSocket.Connected)
            {
                throw new Exception("Disconnected");
            }
            if (buffer == null || buffer.Length == 0)
            {
                throw new Exception("Send buffer empty");
            }
            try
            {
                int sendbytes = m_oSocket.Send(buffer, offset, size, flag);
                if (sendbytes != size)
                {
                    throw new Exception(String.Format("Send data length error(REQ={0} REAL={1})", buffer.Length, sendbytes));
                }
            }
            catch (Exception ex)
            {
                _ = ex.Message;
            }
        }

        public int Receive(byte[] buffer, int offset, int size, SocketFlags flag)
        {
            int nBufferLength = buffer.Length;
            int nRecvLength = m_oSocket.Receive(buffer, offset, size, flag);
            if (nRecvLength != size)
            {
                throw new Exception(String.Format("Recv data length error(REQ={0}, REAL={1})", nBufferLength, nRecvLength));
            }
            return nRecvLength;
        }

        public void AsyncOnReceived(IAsyncResult ar)
        {
            if (m_oSocket == null || !m_oSocket.Connected)
            {
                return;
            }
            try
            {
                int readLength = m_oSocket.EndReceive(ar);
                if (readLength > 0)
                {
                    string receiveData = Encoding.ASCII.GetString(receiveBuff);
                    this.TcpSyncSocketReceive(receiveData);
                    ClearReadBuffer();
                    m_oSocket.BeginReceive(receiveBuff, 0, receiveBuff.Length, SocketFlags.None, AsyncOnReceived, m_oSocket);
                }
            }
            catch (Exception)
            {
            }
        }

        public bool ProtocolConnect()
        {
            if (IsConnected())
            {
                if (m_oSocket.RemoteEndPoint != null)
                {
                    WriteLogInfo($"{m_oSocket.RemoteEndPoint.ToString()}  Is Already Conneted Socket!");
                }
                return true;
            }
            Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                IPEndPoint ep = new IPEndPoint(IPAddress.Parse(IP), PORT);
                if (Asr != null && !Asr.IsCompleted)
                {
                    return false;
                }
                sock.ReceiveTimeout = 5000;
                sock.SendTimeout = 5000;
                Asr = sock.BeginConnect(ep, ProtocolAsyncOnConnected, sock);
                Asr.AsyncWaitHandle.WaitOne(5000, exitContext: true);
            }
            catch (Exception e)
            {
                OnConnect(e);
            }
            return sock.Connected;
        }

        public void SetAcceptSocket(Socket sock)
        {
            m_oSocket = sock;
            ProtocolReceive();
        }

        private void ProtocolAsyncOnConnected(IAsyncResult _ar)
        {
            Socket sock = (Socket)_ar.AsyncState;
            try
            {
                m_oSocket = sock;
                if (m_oSocket.Connected)
                {
                    sock.EndConnect(_ar);
                }
                OnConnect(null);
            }
            catch (Exception ex)
            {
                OnConnect(ex);
            }
        }

        private void ProtocolReceive()
        {
            m_oSocket.BeginReceive(protocolReceiveBuff, 0, protocolReceiveBuff.Length, SocketFlags.None, ProtocolAsyncOnReceived, m_oSocket);
        }

        public void ProtocolAsyncOnReceived(IAsyncResult ar)
        {
            if (m_oSocket == null || !m_oSocket.Connected)
            {
                return;
            }
            try
            {
                int readLength = m_oSocket.EndReceive(ar);
                if (readLength > 0)
                {
                    string receiveData = Encoding.ASCII.GetString(protocolReceiveBuff);
                    receiveData = receiveData.Replace("\0", "");
                    this.TcpSyncSocketReceive(receiveData);
                    PropertocolClearReadBuffer();
                    m_oSocket.BeginReceive(protocolReceiveBuff, 0, protocolReceiveBuff.Length, SocketFlags.None, ProtocolAsyncOnReceived, m_oSocket);
                }
            }
            catch (Exception)
            {
            }
        }

        private void Receive()
        {
            m_oSocket.BeginReceive(protocolReceiveBuff, 0, protocolReceiveBuff.Length, SocketFlags.None, AsyncOnReceived, m_oSocket);
        }
    }

}
